/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.util;

import java.util.LinkedHashMap;
import java.util.Map;

public class AppOption{

	private String appName;
	private Map<String, Option> map = new LinkedHashMap<String, Option>();
	private String[] args;

	public static final class AppOptionException extends RuntimeException {
		private static final long serialVersionUID = 1188658008045795091L;
		private final boolean reqHelp;
		private AppOptionException(String msg, boolean reqHelp) {
			super(msg);
			this.reqHelp = reqHelp;
		}
		public static AppOptionException getInstance(String msg) {
			return new AppOptionException(msg, false);
		}
		static AppOptionException getInstanceOfHelpShown() {
			return new AppOptionException(null, true);
		}
		static AppOptionException getInstanceOfProgError(String msg) {
			final String prefix = "PROGRAM ERROR: ";
			final String fullMsg = prefix + msg;
			System.err.println(fullMsg);
			return new AppOptionException(fullMsg, false);
		}
		public boolean isHelpRequested() {
			return reqHelp;
		}
	}

	protected AppOption(String appName) {

		this.appName = appName;
		this.args = new String[0];

	}

	private void addOption(String optPrefix, Option option) {

		if (map.containsKey(optPrefix) == false) {
			map.put(optPrefix, option);
		} else {
			throw AppOptionException.getInstanceOfProgError(optPrefix + " is duplicated");
		}

	}

	protected void addOption(Option option) {

		addOption(option.getPrefix(), option);

	}

	protected void addRequiredOption(String optPrefix, String optParam,
			String description, String defaultValue) {

		addOption(optPrefix, new Option(optPrefix, optParam, description, defaultValue, true));

	}

	protected void addOption(String optPrefix, String optParam,
			String description, String defaultValue) {

		addOption(optPrefix, new Option(optPrefix, optParam, description, defaultValue));

	}

	protected void addOption(String optPrefix, String optParam, String description) {

		addOption(optPrefix, new Option(optPrefix, optParam, description));

	}

	protected void addRequiredOption(String optPrefix, String description) {

		addOption(optPrefix, new Option(optPrefix, description, true));

	}

	protected void addOption(String optPrefix, String description) {

		addOption(optPrefix, new Option(optPrefix, description));

	}

	protected void deleteOption(String optPrefix) {

		if (map.containsKey(optPrefix)) {
			map.remove(optPrefix);
		}

	}

	protected void setRequired(String optPrefix, boolean required) {

		Option opt = map.get(optPrefix);
		if (opt != null) {
			opt.setRequired(required);
		}

	}

	public void showHelp(){

		StringBuffer msg = new StringBuffer();
		msg.append(appName);

		for (Map.Entry<String, Option> e : map.entrySet()) {
			Option opt = e.getValue();
			StringBuffer sb = new StringBuffer();
			sb.append(opt.getPrefix());
			if (opt.hasParamValue()) {
				if (opt.mustSetParamValue()) {
					sb.append(' ');
					sb.append(opt.getParam());
				} else {
					sb.append(" [");
					sb.append(opt.getParam());
					sb.append(']');
				}
			}
			if (opt.isRequired()) {
				msg.append(" ");
				msg.append(sb);
			} else {
				msg.append(" [");
				msg.append(sb);
				msg.append(']');
			}
		}
		System.out.println(msg.toString());

		for (Map.Entry<String, Option> e : map.entrySet()) {
			System.out.println("  " + e.getValue().toString());
		}

	}

	private void checkParams() {

		for (Map.Entry<String, Option> e : map.entrySet()) {
			Option opt = e.getValue();
			if (opt.isRequired() && !opt.isSet()) {
				StringBuffer msg = new StringBuffer();
				msg.append("missing \"");
				msg.append(opt.getPrefix());
				if (opt.hasParamValue()) {
					msg.append(" ");
					msg.append(opt.getParam());
				}
				msg.append("\"");
				throw AppOptionException.getInstance(msg.toString());
			}
		}

	}

	public void parse(String[] args) {

		this.args = args;

		int i = 0;
		while (i < args.length) {
			String optPrefix = args[i];
			Option opt = map.get(optPrefix);
			if (opt == null) {
				final String[] helps = { "-h", "-help", "--help" };
				for (String key : helps) {
					if (key.equals(optPrefix)) {
						throw AppOptionException.getInstanceOfHelpShown();
					}
				}
				throw AppOptionException.getInstance("Unknown parameter: " + optPrefix);
			}

			if (opt.hasParamValue()) {
				if ((i+1 < args.length) && (args[i+1].startsWith("-") == false)) {
					opt.setValue(args[i+1]);
					i += 2;
					continue;
				} else if (opt.mustSetParamValue()) {
					throw AppOptionException.getInstance("Lost parameter value of " + optPrefix);
				}
			}
			opt.setValue();
			i++;

		}

		checkParams();

	}

	public String getOptionValue(String optPrefix) {

		Option opt = map.get(optPrefix);
		if (opt != null) {
			return opt.getValue();
		} else {
			assert(false);
			String msg = "option key " + optPrefix + " is unusable";
			throw AppOptionException.getInstanceOfProgError(msg);
		}

	}

	public boolean isOptionSet(String optPrefix) {

		Option opt = map.get(optPrefix);
		if (opt != null) {
			return opt.isSet();
		} else {
			assert(false);
			String msg = "option key " + optPrefix + " is unusable";
			throw AppOptionException.getInstanceOfProgError(msg);
		}

	}

	public String[] getArgs() {
		return args;
	}

}

